-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate sample's DateOfBirth field to AgeDateOfBirthField #74
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phew, what a brain excursion... I left some comments on my journey through the code.
src/senaite/patient/api.py
Outdated
@@ -186,6 +188,7 @@ def update_patient(patient, **values): | |||
patient.reindexObject() | |||
|
|||
|
|||
@deprecated("Us senaite.core.api.dtime.to_dt instead") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small typo: Us
-> Use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved with c06ae2b
src/senaite/patient/api.py
Outdated
if not any([years, months, days]): | ||
raise AttributeError("No valid ymd: {}".format(age_ymd)) | ||
if default is _marker: | ||
raise AttributeError("No valid ymd: {}".format(ymd)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better use a TypeError
or ValueError
instead of a AttributeError
. But probably not needed at all if we just return (0, 0, 0)
.
src/senaite/patient/api.py
Outdated
|
||
ymd = list("ymd") | ||
diff = map(str, (delta.years, delta.months, delta.days)) | ||
diff = map(str, (val.years, val.months, val.days)) | ||
age = filter(lambda it: int(it[0]), zip(diff, ymd)) | ||
return " ".join(map("".join, age)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Man, you drive me nuts with constructs like this 🙈
Just to understand:
You are converting the years, months and days to strings. Then you zip them together with ["y", "m", "d"]
to filter out empties, e.g. to get 5y2d
to finally " ".join(map("".join, age))
, which returns 5y2d
again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've seen it in the doctest now, it generates something like 43y 2m 3d
.
As proposed further down, please encapsulate it in an own function, e.g. format_ymd
to do this.
src/senaite/patient/api.py
Outdated
valid = map(lambda p: p in ymd, "ymd") | ||
return any(valid) | ||
values = get_years_months_days(ymd, default=None) | ||
return values is not None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drop the default
handling (also below) in favor of this construct:
def is_ymd(ymd):
try:
get_years_months_days(ymd)
except TypeError:
return False
return True
src/senaite/patient/api.py
Outdated
def get_birth_date(age_ymd, on_date=None): | ||
"""Returns the birth date given an age in ymd format and the date when age | ||
was recorded or current datetime if None | ||
def get_years_months_days(ymd, default=_marker): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the default
handling for simplicity (only used in is_ymd
and get_birth_date
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
src/senaite/patient/api.py
Outdated
days = extract_period(age_ymd, "d") | ||
years = extract_period(ymd, "y") | ||
months = extract_period(ymd, "m") | ||
days = extract_period(ymd, "d") | ||
if not any([years, months, days]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just return (0, 0, 0)
?
E.g. for a new born baby with 2 hours.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, with latest changes it always returns (0, 0, 0)
unless format or type are not valid
src/senaite/patient/api.py
Outdated
years, months, days = get_years_months_days(ymd) | ||
except AttributeError: | ||
if default is _marker: | ||
raise AttributeError("No valid ymd: {}".format(age_ymd)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phew, catch an AttributeError
(which is actually none) to raise an AttributeError
again to (probably) bypass the IndexError
that might happen here years, months, days = get_years_months_days(ymd)
...
I would maybe completely skip the handling here and leave it to the get_birth_date
to either raise a TypeError
or return (0, 0, 0)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I simplified the whole thing and now it raises TypeError
or ValueError
only if the format is not correct
delta = dtime.get_relative_delta(birth_date, on_date) | ||
return to_ymd(delta) | ||
except (ValueError, TypeError): | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You do everywhere the default
handling except of here you return None
? What happened? 😅
Maybe you can skip here the ValueError
. At least I see only the explicit TypeError
that is raised by to_ymd
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
ymd = map(lambda p: value.get(p), ["years", "months", "days"]) | ||
if not any(ymd): | ||
# No values set | ||
return None | ||
return None, {} | ||
|
||
# Age in ymd format | ||
ymd = filter(lambda p: p[0], zip(ymd, 'ymd')) | ||
ymd = "".join(map(lambda p: "".join(p), ymd)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, once again this dance. If it is really required, I would somehow encapsulate it in a format_ymd
function with a good docstring / comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with e77df12 . Is now using to to_ymd
function
super(AgeDateOfBirthField, self).set(instance, val) | ||
|
||
def get_date_of_birth(self, instance): | ||
"""Returns whether the date of birth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incomplete/confusing docstring
Hehe I think so. Apologies for these excursions |
|
||
>>> dob = dtime.to_dt("19791207") | ||
>>> api.get_age_ymd(dob, on_date="20230518") | ||
'43y 5m 11d' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy birthday:)
Description of the issue/feature this PR addresses
This Pull Request makes
senaite.patient
compatible with senaite/senaite.core#2307 and senaite/senaite.core#2311 by replacing theDateTimeField
type for sample'sDateOfBirth
to a new fieldAgeDateOfBirthField
, fully supported by theAgeDoBWidget
.Current behavior before PR
The "DateOfBirth" field was only storing the datetime, while other attributes required for the field and widget to work properly were stored as independent attributes in the instance itself.
Desired behavior after PR is merged
The "DateOfBirth" field stores a tuple (
dob
,from_age
,estimated
).--
I confirm I have tested this PR thoroughly and coded it according to PEP8
and Plone's Python styleguide standards.